from pathlib import Path
from datetime import datetime

from openpyxl import load_workbook, Workbook
from datetime import datetime
from dateutil import parser
from PySide6.QtCore import Qt, QDate, Signal, Slot, QThread, QEventLoop, QTimer
from PySide6.QtWidgets import QLineEdit, QGroupBox, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QComboBox, QSpacerItem,\
                              QSizePolicy, QCheckBox
from PySide6.QtGui import QDoubleValidator

from api import api_ui, api_read, api_convert

class LedgerOneYear(QGroupBox):
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)
    send_to_subthread_signal = Signal(bool)
    request_ledger_signal = Signal()
    log_signal = Signal(str, str)

    SPACER_ITEM = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

    def __init__(self, group_box_style: str):
        super().__init__()
        self.setStyleSheet(group_box_style)
        self.setTitle('一年期筛选')
        self.setup_ui()

    def setup_ui(self):
        # 日期行
        date_row_layout = QHBoxLayout()
        date_label = QLabel('日期')
        date_label.setFixedWidth(90)
        date_label.setAlignment(Qt.AlignCenter)
        wavy_line_label = QLabel('~')
        wavy_line_label.setFixedWidth(30)
        wavy_line_label.setAlignment(Qt.AlignCenter)
        self.start_year_combox = QComboBox()
        self.start_year_combox.setFixedWidth(100)
        self.start_year_combox.setObjectName('start_year_combo')
        self.start_month_combox = QComboBox()
        self.start_month_combox.setObjectName('start_month_combo')
        self.start_month_combox.setFixedWidth(100)
        self.end_year_combox = QComboBox()
        self.end_year_combox.setFixedWidth(100)
        self.end_year_combox.setObjectName('end_year_combo')
        self.end_month_combox = QComboBox()
        self.end_month_combox.setFixedWidth(100)
        self.end_month_combox.setObjectName('end_month_combo')
        current_date = QDate.currentDate()
        current_year = current_date.year()
        current_month = current_date.month()
        for year in range(2024, current_year + 1):
            self.start_year_combox.addItem(str(year))
            self.end_year_combox.addItem(str(year))
        for month in range(1, 13):
            self.start_month_combox.addItem(f'{month:02d}')
            self.end_month_combox.addItem(f'{month:02d}')
        self.end_year_combox.setCurrentText(str(current_year))
        self.end_month_combox.setCurrentText(f'{current_month:02d}')
        self.start_year_combox.currentTextChanged.connect(self.check_date)
        self.start_month_combox.currentTextChanged.connect(self.check_date)
        self.end_year_combox.currentTextChanged.connect(self.check_date)
        self.end_month_combox.currentTextChanged.connect(self.check_date)

        date_row_layout.addWidget(date_label)
        date_row_layout.addWidget(self.start_year_combox)
        date_row_layout.addWidget(self.start_month_combox)
        date_row_layout.addWidget(wavy_line_label)
        date_row_layout.addWidget(self.end_year_combox)
        date_row_layout.addWidget(self.end_month_combox)
        date_row_layout.addItem(self.SPACER_ITEM)

        # 银行行
        bank_row_layout, (self.all_bank_ckbox, self.icbc_ckbox, self.abc_ckbox, self.ccb_ckbox) = api_ui.checkbox_ui('银行', ('全部', '工商银行', '农业银行', '建设银行'))
        self.all_bank_ckbox_list = [self.all_bank_ckbox, self.icbc_ckbox, self.abc_ckbox, self.ccb_ckbox]
        for ckbox in self.all_bank_ckbox_list:
            ckbox.clicked.connect(self.check_bank)

        # 金额行
        amount_row_layout = QHBoxLayout()
        amount_label = QLabel('金额')
        amount_label.setFixedWidth(90)
        amount_label.setAlignment(Qt.AlignCenter)
        wavy_line_label = QLabel('~')
        wavy_line_label.setFixedWidth(30)
        wavy_line_label.setAlignment(Qt.AlignCenter)
        self.start_amount_edit = QLineEdit()
        self.start_amount_edit.setFixedWidth(100)
        self.start_amount_edit.setValidator(QDoubleValidator(0.0, 100000000.0, 2, self))
        self.end_amount_edit = QLineEdit()
        self.end_amount_edit.setFixedWidth(100)
        self.end_amount_edit.setValidator(QDoubleValidator(0.0, 100000000.0, 2, self))
        self.start_amount_edit.editingFinished.connect(self.check_amount)
        self.end_amount_edit.editingFinished.connect(self.check_amount)
        amount_row_layout.addWidget(amount_label)
        amount_row_layout.addWidget(self.start_amount_edit)
        amount_row_layout.addWidget(wavy_line_label)
        amount_row_layout.addWidget(self.end_amount_edit)
        amount_row_layout.addItem(self.SPACER_ITEM)

        # 结束后汇总
        self.one_more_thing_ckbox = QCheckBox('结束后汇总各项目至一个文件')
        self.one_more_thing_ckbox.setStyleSheet("margin-top: 5px; margin-left: 35px;")  # 设置左边距

        # 功能提示
        tip_label = QLabel('根据银行和金额筛选暂时不可用，默认全部银行和不限金额')

        # 开始按钮
        self.start_btn = QPushButton('开始')
        self.start_btn.clicked.connect(self.check_input)

        main_layout = QVBoxLayout(self)
        main_layout.addLayout(date_row_layout)
        main_layout.addLayout(bank_row_layout)
        main_layout.addLayout(amount_row_layout)
        main_layout.addWidget(self.one_more_thing_ckbox, stretch=10)
        main_layout.addWidget(self.start_btn, alignment=Qt.AlignCenter)
        main_layout.addWidget(tip_label, alignment=Qt.AlignCenter)
    
    def run_thread(self):
        self.start_btn.setEnabled(False)
        self.start_btn.setText('运行中')
        self.rel_tip_signal.emit('开始', 'blue')

        start_date = f'{self.start_year_combox.currentText()}-{self.start_month_combox.currentText()}'
        end_date = f'{self.end_year_combox.currentText()}-{self.end_month_combox.currentText()}'
        need_collect = True if self.one_more_thing_ckbox.isChecked() else False
        self.thread = OneYearThread(self.ledger_path, start_date, end_date, need_collect)
        self.thread.rel_tip_signal.connect(self.rel_tip_signal)
        self.thread.show_msg_signal.connect(self.show_msg_signal)
        self.thread.finished_signal.connect(self.after_finish)
        self.thread.log_signal.connect(lambda x: self.log_signal.emit(__name__, x))
        self.thread.start()

    @Slot()
    def check_input(self):
        self.request_ledger_signal.emit()
        if self.ledger_path == None:
            self.rel_tip_signal.emit('台账路径为空，结束运行', 'red')
            return
        try:
            self.run_thread()
        except Exception as e:
            self.rel_tip_signal.emit(f'发生未知错误：{e}', 'red')
            self.after_finish(0)

    @Slot()
    def check_date(self):
        start_year = int(self.start_year_combox.currentText())
        end_year = int(self.end_year_combox.currentText())
        start_month = int(self.start_month_combox.currentText())
        end_month = int(self.end_month_combox.currentText())
        current_combox = self.sender()
        if current_combox.objectName().startswith('start'):
            if start_year > end_year:
                self.end_year_combox.setCurrentText(str(start_year))
                if start_month > end_month:
                    self.end_month_combox.setCurrentText(f'{start_month:02d}')
            if start_year == end_year and start_month > end_month:
                self.end_month_combox.setCurrentText(f'{start_month:02d}')
        else:
            if end_year < start_year:
                self.start_year_combox.setCurrentText(str(end_year))
                if end_month < start_month:
                    self.start_month_combox.setCurrentText(f'{end_month:02d}')
            if end_year == start_year and end_month < start_month:
                self.start_month_combox.setCurrentText(f'{end_month:02d}')
    
    @Slot()
    def check_bank(self):
        bank_ckbox_list = self.all_bank_ckbox_list.remove()
        current_ckbox = self.sender()
        if current_ckbox == self.all_bank_ckbox:
            if self.all_bank_ckbox.isChecked():
                for ckbox in bank_ckbox_list:
                    ckbox.setChecked(False)
        else:
            if self.icbc_ckbox.isChecked() and self.abc_ckbox.isChecked() and self.ccb_ckbox.isChecked():
                self.all_bank_ckbox.setChecked(True)
                for ckbox in bank_ckbox_list:
                    ckbox.setChecked(False)
            else:
                self.all_bank_ckbox.setChecked(False)
        
    @Slot()
    def check_amount(self):
        start_amount = api_convert(self.start_amount_edit.text(), divisor=1)
        self.start_amount_edit.setText(str(start_amount))
        end_amount = api_convert(self.end_amount_edit.text(), divisor=1)
        self.end_amount_edit.setText(str(end_amount))
        current_edit = self.sender()
        if current_edit == self.start_amount_edit:
            if start_amount > end_amount:
                self.end_amount_edit.setText(str(start_amount))
        else:
            if end_amount < start_amount:
                self.start_amount_edit.setText(str(end_amount))

    @Slot(int)
    def after_finish(self, sign: int):
        self.start_btn.setText('开始')
        self.start_btn.setEnabled(True)
        if sign:
            self.rel_tip_signal.emit('完成', 'green')
            self.rel_tip_signal.emit('-'*99, 'green')
        else:
            self.rel_tip_signal.emit('提前结束', 'red')

    @Slot(Path)
    def receive_ledger_path(self, ledger_path: Path):
        self.ledger_path = ledger_path


class OneYearThread(QThread):
    finished_signal = Signal(int)
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)
    subthread_finished_signal = Signal()
    log_signal = Signal(str)
    # value_signal = Signal(dict)

    def __init__(self, ledger_path: Path, start_date: str, end_date: str, need_collect: bool):
        super().__init__()
        self.ledger_path = ledger_path
        self.start_date = start_date
        self.end_date = end_date
        self.need_collect = need_collect

        self.is_continue = True

    def parse_date(self, date_str):
        if isinstance(date_str, datetime):  # 已经是datetime直接返回
            return date_str
        try:  
            # 使用dateutil.parser.parse来尝试解析各种格式的日期字符串  
            return parser.parse(date_str)
        except ValueError:
            # 如果解析失败，返回None  
            return None  

    def run(self):
        self.all_programme_info = {}         # 所有项目的一年期付款信息
        get_files = api_read.GetFiles(self.ledger_path, '结算支付台账')
        get_files.rel_tip_signal.connect(self.rel_tip_signal)
        get_files.finished_signal.connect(self.subthread_finished)
        ledger_files = get_files.run()
        loop = QEventLoop()         # 创建一个事件循环，等待get_files运行结束后给self.is_continue赋值
        self.subthread_finished_signal.connect(loop.quit)
        loop.exec()
        if self.is_continue == False:
            self.finished_signal.emit(0)
            return
        task_num = len(ledger_files)
        progress_num = 1
        for programme_name, ledger_file in ledger_files.items():
            payment_info = {}       # 所有一年期付款信息
            read_ledger = api_read.ReadLedger(ledger_file)
            read_ledger.rel_tip_signal.connect(self.rel_tip_signal)
            read_ledger.finished_signal.connect(self.subthread_finished)
            read_ledger.log_signal.connect(self.log_signal)
            ledger_content = read_ledger.run()
            if ledger_content == None:
                self.finished_signal.emit(0)
                return
            for unit_name, unit_content in ledger_content.items():      # 遍历汇总表的每行数据
                one_year_amount = unit_content['一年期付款合计']
                if one_year_amount != '0':               # 如果存在一年期付款
                    read_ledger = api_read.ReadLedger(ledger_file, unit_content['往来单位名称'])
                    read_ledger.rel_tip_signal.connect(self.rel_tip_signal)
                    read_ledger.finished_signal.connect(self.subthread_finished)
                    read_ledger.log_signal.connect(self.log_signal)
                    unit_schedules = read_ledger.run()
                    loop = QEventLoop()
                    self.subthread_finished_signal.connect(loop.quit)
                    QTimer.singleShot(1000, loop.quit)
                    loop.exec()
                    if unit_schedules == None:
                        self.finished_signal.emit(0)
                        continue
                    for row_name, row_content in unit_schedules.items():        # 遍历明细表的每行数据
                        if row_name == '合计':
                            continue
                        key_info = {}       # 存放需要收集的数据
                        if row_content['一年期付款合计'] != '0':
                            key_info['key'] = row_name
                            key_info['往来单位名称'] = unit_name
                            key_info['凭证日期'] = row_content['凭证日期']
                            key_info['工行一年期'] = row_content['工行一年期']
                            key_info['农行一年期'] = row_content['农行一年期']
                            key_info['建行一年期'] = row_content['建行一年期']
                            key_info['一年期付款合计'] = row_content['一年期付款合计']
                            key_info['备注'] = row_content['备注']
                            payment_info[unit_name] = payment_info.get(unit_name, []) + [key_info]
                    self.test_data(unit_name, one_year_amount, payment_info[unit_name])       # 检查汇总表中的一年期付款合计和明细表中的和是否一致
            # 开始写入
            wb = load_workbook(ledger_file)
            ws = wb['一年期汇总']
            # 获取第一行的信息，填充开始和结束日期
            first_row_info = {}
            for cell in ws[1]:
                cell_value = cell.value
                if cell_value == None:
                    continue
                first_row_info[cell.value] = cell.column
            reference_cell = ws.cell(row=1, column=first_row_info['开始日期：'])
            ws.cell(row=1, column=reference_cell.column+1).value = self.start_date
            reference_cell = ws.cell(row=1, column=first_row_info['截止日期：'])
            ws.cell(row=1, column=reference_cell.column+1).value = self.end_date
            # 根据开始和结束日期筛选付款信息
            parsed_start = self.parse_date(self.start_date)
            parsed_end = self.parse_date(self.end_date)
            target_info = []
            for unit_name, info in payment_info.items():        # info: [dict, dict,,,]
                for row_info in info:
                    row_date = row_info['凭证日期']
                    if row_date == 'None':
                        row_info['凭证日期'] = '日期空白'
                        target_info.append(row_info)
                    else:
                        row_date = self.parse_date(row_date)
                        if row_date is None:         # 解析失败，稳妥起见，判定为符合条件
                            target_info.append(row_info)
                            continue
                        if parsed_start <= row_date <= parsed_end:
                            target_info.append(row_info)
            ws.delete_rows(3, ws.max_row - 2)      # 清空第三行到尾行的数据
            cur_row = 3
            headers_info = {}
            for cell in ws[2]:
                headers_info[cell.value] = cell.column
            for info in target_info:
                for key, value in info.items():
                    if value == 'None':
                        continue
                    ws.cell(row=cur_row, column=headers_info[key], value=value)
                cur_row += 1
            
            wb.save(ledger_file)
            self.rel_tip_signal.emit(f'{programme_name} 已筛选完毕 [{progress_num}/{task_num}]', 'blue')
            progress_num += 1
            api_read.just_open(ledger_file)
            self.all_programme_info[programme_name] = payment_info
        if self.need_collect:
            self.collect()
        self.finished_signal.emit(1)
    
    @Slot(int)
    def subthread_finished(self, sign: int):
        if sign == 0:
            self.is_continue = False
        self.subthread_finished_signal.emit()

    def collect(self):
        root_path = self.ledger_path
        wb = Workbook()
        ws = wb.active
        ws.append(['开始日期：', self.start_date, '截止日期：', self.end_date])
        ws.append(['项目', 'key', '往来单位名称', '凭证日期', '工行一年期', '农行一年期', '建行一年期', '一年期付款合计', '备注'])
        for programme, payment_info in self.all_programme_info.items():
            for unit, info in payment_info.items():
                for row_info in info:
                    cell_values = [programme] + list(row_info.values())
                    ws.append(cell_values)
        cur_time = datetime.now().strftime('%Y%m%d-%H%M%S')
        file = root_path / f'一年期汇总{cur_time}.xlsx'
        wb.save(file)
        self.rel_tip_signal.emit(f'已汇总到 {file}', 'green')
    
    def test_data(self, unit_name: str, one_year_amount: str, info: dict):
        sum1 = 0
        for row_data in info:
            sum1 += api_convert.convert_to_wan(row_data['一年期付款合计'], divisor=1)
        sum2 = api_convert.convert_to_wan(one_year_amount, divisor=1)
        if sum1 != sum2:
            self.rel_tip_signal.emit(f'提示：{unit_name} 汇总表一年期付款合计（{sum2}）与明细表中的和（{sum1}）不一致', 'red')